﻿namespace Blaze.Utilities
{
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using Framework;
    using Ionic.Zip;

    /// <summary>
    /// 在另外一个线程中批量解压zip文件。
    /// </summary>
    public class BatchZipExtractor
    {
        private struct Task
        {
            public ZipFile From;
            public string To;
        }

        /// <summary>
        /// 获取一个值，表示是否解压已完成。
        /// </summary>
        public bool IsDone { get; private set; }

        /// <summary>
        /// 获取最后一次产生的错误信息。
        /// </summary>
        public string LastError { get; private set; }

        /// <summary>
        /// 获取解压进度。
        /// </summary>
        public float Progress { get; private set; }

        public BatchZipExtractor()
        {
            mThread = new Thread(run);
        }

        /// <summary>
        /// 增加一个解压任务。
        /// </summary>
        /// <param name="srcPath">zip文件路径</param>
        /// <param name="dstPath">目标文件夹路径</param>
        public void Add(string srcPath, string dstPath)
        {
            var task = new Task
            {
                From = ZipFile.Read(srcPath),
                To = dstPath,
            };
            mTasks.Add(task);
            mPendingEntriesCount += task.From.Count;
        }

        /// <summary>
        /// 启动解压。
        /// </summary>
        public void Start()
        {
            if (mTasks.Count == 0)
            {
                IsDone = true;
                Progress = 1.0f;
                return;
            }

            mThread.Start();
        }

        private void onProgress(object sender, ExtractProgressEventArgs e)
        {
            if (e.EventType != ZipProgressEventType.Extracting_AfterExtractEntry)
                return;
            mExtractedEntriesCount++;
            Progress = (float) mExtractedEntriesCount/mPendingEntriesCount;
        }

        private void run()
        {
            try
            {
                for (var i = 0; i < mTasks.Count; i++)
                {
                    var task = mTasks[i];
                    FileUtility.EnsureDirectory(task.To);
                    using (var zipFile = task.From)
                    {
                        zipFile.ExtractProgress += onProgress;
                        zipFile.ExtractAll(task.To, ExtractExistingFileAction.OverwriteSilently);
                    }
                    Progress = 1.0f;
                }
            }
            catch (Exception e)
            {
                LastError = e.Message;
            }
            finally
            {
                IsDone = true;
            }
        }

        private int mExtractedEntriesCount;
        private int mPendingEntriesCount;
        private readonly List<Task> mTasks = new List<Task>();
        private readonly Thread mThread;
    }
}